<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>let 命令</h1>
    <p>ES6 新增了 let 命令,用来声明变量。它的用法类似于 var,但是所声明的变量,只在 let 命令所在的代码块内有效.</p>
    <h3>1. let 声明的变量只在 let 命令所在的代码块内有效</h3>
    <p>
        下面的代码如果使用 let 命令,就会报错
    </p>
    <pre>
        {
            let a = 10;
            var b = 1;
        }
        a // a is not defined
        b // 1
    </pre>
    <h3>2. 不存在变量提升</h3>
    <p>
        var 命令会发生“变量提升”现象，即变量可以在声明之前使用，值为 undefined。这种现象多多少少是有些奇怪的,按照一般的逻辑,变量应该在声明语句之后才可以使用。
        为了纠正这种现象,let 命令改变了语法行为,它所声明的变量一定要在声明后使用,否则报错。
    </p>
    <pre>
        // var 的情况
        console.log(foo); // undefined
        var foo = 2;

        // let 的情况
        console.log(bar); // 报错ReferenceError
        let bar = 2;
    </pre>
    <h3>3. 暂时性死区</h3>
    <p>
        只要块级作用域内存在 let 命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。
        ES6 明确规定,如果区块中存在 let 和 const 命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
        总之,在代码块内,使用 let 命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。
    </p>
    <pre>
        var tmp = 123;
        if (true) {
            tmp = 'abc'; // ReferenceError
            let tmp;
        }
    </pre>
    <h3>4. 不允许重复声明</h3>
    <p>
        let 不允许在相同作用域内,重复声明同一个变量。
    </p>
    <pre>
        // 报错
        function func() {
            let a = 10;
            var a = 1;
        }

        // 报错
        function func() {
            let a = 10;
            let a = 1;
        }
    </pre>
    <h3>5. 块级作用域</h3>
    <p>
        函数内部使用 let 定义变量,则这个变量只能在函数内部使用。
        函数外部使用 let 定义变量,则这个变量可以在函数内部和函数外部使用。
    </p>
    <pre>
        function f1() {
            let n = 5;
            if (true) {
                let n = 10;
            }
            console.log(n); // 5
        }
        function f2() {
            let n = 5;
            if (true) {
                let n = 10;
            }
            console.log(n); // 5
        }
    </pre>
    <h3>6. 全局声明的 let 变量，不具有全局作用域</h3>
    <p>
        全局使用 let 声明的变量，不会变成全局对象的属性
    </p>
    <pre>
        var a = 1;
        let b = 1;
        window.a // 1
        window.b // undefined
    </pre>
    <h3>7. 循环中的 let 变量</h3>
    <p>
        循环中的 let 变量
    </p>
    <pre>
        var a = [];
        for (let i = 0; i < 10; i++) {
            a[i] = function () {
                console.log(i);
            };
        }
        a[6](); // 6
    </pre>
    <script>
        // let 命令
        // 1. let 声明的变量只在 let 命令所在的代码块内有效
        {
            let a = 10;
            var b = 1;
        }
        // a 是 let 声明的变量，只在它所在的代码块有效
        //console.log(a); // a is not defined
        // b 是 var 命令声明的变量，在代码块之外也是有效果的
        console.log(b); // 1
    </script>
</body>
</html>